﻿using System;
using System.Collections;
using System.Text;
using Evaluant.NLinq.Expressions;
using System.Collections.Generic;

namespace Evaluant.NLinq
{
    public class NLinqQuery
    {
        protected string expression;
        protected Expression linqExpression;

        protected NLinqQuery()
        {
            Parameters = new Dictionary<string, object>();
        }

        public NLinqQuery(Expression expression)
            : this()
        {
            if (expression == null)
                throw new ArgumentNullException("Expression can't be empty", "expression");

            linqExpression = expression;
        }


        public NLinqQuery(Expression expression, Dictionary<string, object> parameters)
            : this(expression)
        {
            if (parameters != null)
                Parameters = parameters;
        }

        public NLinqQuery(string expression)
            : this()
        {
            if (expression == null || expression == String.Empty)
                throw new ArgumentNullException("Expression can't be empty", "expression");

            this.expression = expression;

            Parse();
        }

        public NLinqQuery(string expression, Dictionary<string, object> parameters)
            : this(expression)
        {
            Parameters = parameters;
        }

        public static Parser GetParser()
        {
            Parser parser = new Parser(new Lexer());

            return parser;
        }

        protected void Parse()
        {
            linqExpression = GetParser().Parse(expression);
        }

        public Expression Expression
        {
            get { return linqExpression; }
        }

        public Dictionary<string, object> Parameters { get; protected set; }

        public void SetParameter(string name, object value)
        {
            Parameters[name] = value;
        }

        public NLinqQuery Page(int first, int max)
        {
            Expression exp = Expression;
            if (exp.ExpressionType == ExpressionTypes.MemberAccess && ((MemberExpression)exp).Statement.ExpressionType == ExpressionTypes.Call)
            {
                if (((MethodCall)((MemberExpression)exp).Statement).IsAggregate())
                    return this;
            }
            return new NLinqQuery(new MemberExpression(new MethodCall("Take", new ValueExpression(first, TypeCode.Int32)),
                new MemberExpression(new MethodCall("Skip", new ValueExpression(max, TypeCode.Int32)), exp)), Parameters);
        }

        public NLinqQuery Where(string constraint)
        {
            Expression constraintExpression = GetParser().Parse(constraint);
            return Where(constraintExpression);
        }

        public NLinqQuery Where(Expressions.Expression constraintExpression)
        {
            if (constraintExpression.ExpressionType == ExpressionTypes.MemberAccess || constraintExpression.ExpressionType == ExpressionTypes.QueryBody)
                return new NLinqQuery(constraintExpression);
            if (constraintExpression.ExpressionType != ExpressionTypes.Lambda)
                throw new NotSupportedException();
            return new NLinqQuery(new MemberExpression(new MethodCall("Where", constraintExpression), Expression), Parameters);
        }
    }
}
